# -*- coding:utf-8 -*-
import socket
import struct
import select
import sys
import os
import time
import threading
import queue
import logging
import logging.handlers

__version__ = '0.1.0 mobilefzb'

class ConnectionHandler(threading.Thread):
	def __init__(self,m_queue):
		threading.Thread.__init__(self)
		self._queue = m_queue
	def run(self):
		self.client_buffer = ''
		self.client_buffer_len = 8192
		self.server_version = 'smart proxy/' + __version__
		self.proxy_httpver = 'HTTP/1.1'
		log_file = cur_file_dir() + os.sep + 'm_proxy.log'
		self.logger = logging.getLogger(log_file)
		while True:
			(self.client,_,self.timeout) = self._queue.get()
			self.method,self.path,self.protocol = self.get_base_header()
			#self.logger.debug('method:%s,path:%s,protocol:%s'%(self.method,self.path,self.protocol))
			if self.method == 'CONNECT':
				self.method_connect()
			elif self.method in ('OPTIONS','GET','HEAD','POST','PUT','DELETE','TRACE'):
				self.method_others()
			self.client.close()
			self.target.close()
	def get_base_header(self):
		while True:
			self.client_buffer += self.client.recv(self.client_buffer_len).decode('utf-8')
			end = self.client_buffer.find('\n')
			if end != -1:
				break
		self.logger.debug('%s'%(self.client_buffer[:end]))
		data = (self.client_buffer[:(end + 1)]).split(' ')
		self.client_buffer = self.client_buffer[(end + 1):]
		return data
	def method_connect(self):
		self._connect_target(self.path)
		send_msg_buf = self.proxy_httpver + \
			' 200 Connection established\r\n' + \
			'Proxy-agent: %s\r\n\r\n'%(self.server_version)
		self.client.send(send_msg_buf.encode('utf-8'))
		self.client_buffer = ''
		self._read_write()
	def method_others(self):
		self.path = self.path[7:]
		i = self.path.find('/')
		host = self.path[:i]
		path = self.path[i:]
		self.logger.debug('target host: %s'%(host))
		self._connect_target(host)
		send_msg_buf = '%s %s %s'%(self.method,path,self.protocol) + \
								self.client_buffer
		self.logger.debug('send msg buf: %s'%(send_msg_buf))
		self.target.send(send_msg_buf.encode('utf-8'))
		self.client_buffer = ''
		self._read_write()
	def _connect_target(self,host):
		i = host.find(':')
		if i != -1:
			port = int(host[(i + 1):])
			host = host[:i]
		else:
			port = 80
		(soc_family,_,_,_,address) = socket.getaddrinfo(host,port)[0]
		self.target = socket.socket(soc_family)
		self.target.connect(address)
		
	def _read_write(self):
		time_out_max = self.timeout / 3
		socs = [self.client,self.target]
		count = 0
		while True:
			count += 1 
			(recv,_,error) = select.select(socs,[],socs,3)
			if error:
				break
			if recv:
				for in_ in recv:
					data = in_.recv(self.client_buffer_len)
					if in_ is self.client:
						out = self.target
					else:
						out = self.client
					if data:
						out.send(data)
						count = 0
			if count == time_out_max:
				break

def start_server(host = 'localhost',port = 18080,IPv6 = False,
		timeout = 60,handler = ConnectionHandler):
	log_file = cur_file_dir() + os.sep + 'm_proxy.log'
	logger = logging.getLogger(log_file)
	if IPv6 == True:
		soc_type = socket.AF_INET6
	else:
		soc_type = socket.AF_INET
	soc = socket.socket(soc_type)
	soc.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
	soc.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEPORT,1)
	soc.bind((host,port))
	logger.debug('Serving on %s:%d.'%(host,port))
	soc.listen(200)
	#接收数据启动线程
	queue_len = 10 
	m_queue = queue.LifoQueue(queue_len)
	worker = ConnectionHandler(m_queue)
	worker.start()
	while True:
		(connection,address) = soc.accept()
		addr_ip = address[0]
		addr_port = socket.htons(address[1])
		logger.debug('get address %s:%d'%(addr_ip,addr_port))
		m_queue.put((connection,address,timeout))
	worker.join()
	
		
def cur_file_dir():
	path = sys.path[0]
	if os.path.isdir(path):
		return path
	elif os.path.isfile(path):
		return os.path.dirname(path)
		
def init_logger():
	log_file = cur_file_dir() + os.sep + 'm_proxy.log'
	handler = logging.handlers.RotatingFileHandler(log_file,maxBytes = 1024*1024*1024,backupCount = 50)
	fmt = '%(asctime)s - %(filename)s:%(lineno)s - %(name)s - %(message)s'
	formatter = logging.Formatter(fmt)
	handler.setFormatter(formatter)
	logger = logging.getLogger(log_file)
	logger.addHandler(handler)
	logger.setLevel(logging.DEBUG)

if __name__ == '__main__':
	init_logger()
	start_server()
	
